home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d1 / megabyte.arc / VDISK-H.ASM < prev   
Encoding:
Assembly Source File  |  1987-12-12  |  16.6 KB  |  477 lines

  1. PAGE 76,132
  2. TITLE VDISK-H.ASM  Installable Device Driver for Vdisk in High Memory
  3.  
  4. ;*************************************************************************
  5. ; AN INSTALLABLE DEVICE DRIVER TO CREATE A RAM DISK IN MEMORY ABOVE DOS, *
  6. ;      OCCUPYING PAGES C and D (128K capacity).                          *
  7. ;           by J. D. Kronman, based on the IBM example                   *
  8. ;             driver in the PC-DOS technical reference manual            *
  9. ; last revision 11/15/85                                                 *
  10. ;*************************************************************************
  11.  
  12. ;  This driver differs from the IBM driver because it utilizes memory outside
  13. ;  of the range normally used by DOS.  You can take advantage of this if 
  14. ;  you DON'T have an XT that has ROM code for the controller in Page C, etc. 
  15. ;  and you could even make the VDISK 192K if pages C, D, and E are all free.  
  16. ;  I am using a MicroLog Baby Blue Plus and chose to keep Page E free for the 
  17. ;  Z-80.  The following modifications were made to the original IBM driver:
  18. ;
  19. ;  1. The INIT routine was completely rewritten; it was also relocated so 
  20. ;     that the memory it occupies is released after INIT has completed its task.
  21. ;  2. A signon message was added.
  22. ;  3. Three new "dummy" entries were added to the function table for 
  23. ;     DOS 3.x compatibility.
  24. ;  4. The "unimplemented functions" now just exit; in the original version, 
  25. ;     they caused the read code to be executed.
  26. ;
  27. ;  I am fairly new to the world of DOS drivers, so if I made any mistakes, 
  28. ;  I would appreciate hearing from you.  I might code again and everyone will 
  29. ;  benefit if I am making only new mistakes!
  30. ;
  31. ;  Jim Kronman
  32. ;  Bacchus Data Services
  33. ;  6085 Venice Blvd., No. 16
  34. ;  Los Angeles, CA 90034  (213) 558-3281
  35. ;  CompuServe 76703,431
  36. ;
  37. ;--------------------------
  38.  
  39. CSEG            SEGMENT PARA PUBLIC 'CODE'
  40.  
  41. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  42. ;     MACRO FOR STATUS
  43. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  44.  
  45. STATUS  MACRO   STATE,ERR,RC
  46.     IFIDN   <STATE>,<DONE>
  47.           OR    ES:WORD PTR SRH_STA_FLD[BX],0100H
  48.         ENDIF
  49.         IFIDN   <STATE>,<BUSY>
  50.           OR    ES:WORD PTR SRH_STA_FLD[BX],0200H
  51.         ENDIF
  52.         IFIDN   <ERR>,<ERROR>
  53.           OR    ES:WORD PTR SRH_STA_FLD[BX],1000H
  54.         ENDIF
  55.         IFNB    <RC>
  56.           OR    ES:WORD PTR SRH_STA_FLD[BX],RC
  57.         ENDIF
  58.         ENDM
  59.  
  60. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  61. ;         EQUATES
  62. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  63.  
  64. CR        equ    0Dh
  65. LF        equ    0Ah
  66.  
  67. ; READ/WRITE
  68.  
  69. SRH             EQU     0               ;Static request header start
  70. SRH_LEN         EQU     13              ;   "     "       "    length
  71. SRH_LEN_FLD     EQU     SRH             ;   "     "       "      "    field
  72. SRH_UCD_FLD     EQU     SRH+1           ;   "     "       "    Unit code
  73. SRH_CCD_FLD     EQU     SRH+2           ;   "     "       "   command code fld
  74. SRH_STA_FLD     EQU     SRH+3           ;   "     "       "   status field
  75. SRH_RES_FLD     EQU     SRH+5           ;   "     "       "reserved area field
  76.  
  77. MD              EQU     SRH+SRH_LEN     ;Media description byte
  78. MD_LEN          EQU     1               ;   "      "        "   length
  79. DTA             EQU     MD+MD_LEN       ;disk transfer address
  80. DTA_LEN         EQU     4               ;dta length
  81. COUNT           EQU     DTA+DTA_LEN     ;byte/sector count
  82. COUNT_LEN       EQU     2               ;  "    "      "   length
  83. SSN             EQU     COUNT+COUNT_LEN ;starting sector number
  84. SSN_LEN         EQU     2               ;   "       "      "    length
  85.  
  86. ; MEDIA CHECK
  87.  
  88. RET_BYTE        EQU     MD+MD_LEN       ;byte returned from driver
  89.  
  90. ; BUILD B(ios) P(arameter) B(lock)
  91.  
  92. BPBA_PTR        EQU     DTA+DTA_LEN     ;Pointer to BPB
  93. BPBA_PTR_LEN    EQU     4               ;   "    "   "  length
  94.  
  95. ; INIT
  96.  
  97. UNITS           EQU     SRH+SRH_LEN
  98. UNITS_LEN       EQU     1
  99. BR_ADDR_0       EQU     UNITS+UNITS_LEN
  100. BR_ADDR_1       EQU     BR_ADDR_0+2
  101. BR_ADDR_LEN     EQU     4
  102. BPB_PTR_OFF     EQU     BR_ADDR_0+BR_ADDR_LEN
  103. BPB_PTR_SEG     EQU     BPB_PTR_OFF+2
  104.  
  105. ;============== C O D E   B E G I N S   H E R E ================
  106.  
  107. VDSK    PROC    FAR             ;Start Virtual disk procedure
  108.         ASSUME  CS:CSEG,ES:CSEG,DS:CSEG
  109.  
  110. BEGIN:
  111. START   EQU     $
  112.  
  113. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  114. ;   DEVICE HEADER
  115. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  116.  
  117. NEXT_DEV        DD      -1              ;Pointer to next device
  118. ATTRIBUTE       DW      2000H           ;Block device(non-IBM format)
  119. STRATEGY        DW      DEV_STRATEGY    ;pointer to DeviceStrategy
  120. INTERRUPT       DW      DEV_INT         ;pointer to device interrupt handler
  121. DEV_NAME        DB      1               ;Number of block devices
  122.                 DB      7 DUP(?)        ;7 Bytes of filler
  123.  
  124. RH_OFF  DW      ?       ;Request Header offset
  125. RH_SEG  DW      ?       ;Request Header segment
  126.  
  127. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  128. ;    BIOS PARAMETER BLOCK
  129. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  130.  
  131. BPB    equ    $
  132.     dw    512    ;Sector size
  133.     db    1    ;Sectors/allocation unit
  134.     dw    1    ;Number of reserved sectors
  135.     db    2    ;Number of FATS
  136.     dw    64    ;Number of directory entries
  137.     dw    256    ;Total sectors (128kb * 2 sec/kb)
  138.     db    0FCh    ;Media descriptor (like 1 side 9 spt)
  139.     dw    2    ;No.sectors occupied by FAT
  140.  
  141. BPB_PTR    dw    BPB    ;BPB pointer array (1 entry)
  142.  
  143. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  144. ;  VARIABLES
  145. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  146.  
  147. TOTAL       DW      ?    ;Total sectors to transfer
  148. VERIFY          DB      0    ;Verify  1=Yes, 0 = No
  149. START_SEC       DW      0    ;Starting sector number
  150. VDISK_PTR       DW      0C000h    ;Starting segment of virtual disk
  151. USER_DTA        DD      ?    ;Ptr to callers disk transfer address
  152.  
  153. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  154. ;     FUNCTION TABLE
  155. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  156.  
  157. FUNTAB    LABEL   BYTE
  158.     DW      INIT            ;initialization
  159.     DW      MEDIA_CHECK     ;Media check ( block only)
  160.     DW      BUILD_BPB       ;Build BPB   ( block only)
  161.     DW      IOCTL_IN        ;IOCTL input
  162.     DW      INPUT           ;Input(read)
  163.     DW      NO_INPUT        ;non/dest.input,no wait(chr only)
  164.     DW      IN_STAT         ;Input status
  165.     DW      IN_FLUSH        ;Input flush
  166.     DW      OUTPUT          ;Output(write)
  167.     DW      OUT_VERIFY      ;Output(write)with verify
  168.     DW      OUT_STAT        ;Output status
  169.     DW      OUT_FLUSH       ;Output flush
  170.     DW      IOCTL_OUT       ;IOCTL output
  171.     dw    DOS3        ;DOS3 uses this
  172.     dw    DOS3        ;DOS3 uses this
  173.     dw    DOS3        ;DOS3 uses this
  174.  
  175. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  176. ;   LOCAL PROCEDURES
  177. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  178.  
  179. IN_SAVE PROC    NEAR
  180.         MOV     AX,ES:WORD PTR DTA[BX]  ;Save callers DTA
  181.         MOV     WORD PTR CS:USER_DTA,AX
  182.         MOV     AX,ES:WORD PTR DTA+2[BX]
  183.         MOV     WORD PTR CS:USER_DTA+2,AX
  184.         MOV     AX,ES:WORD PTR COUNT[BX];get number of sectors to read
  185.         XOR     AH,AH
  186.         MOV     CS:TOTAL,AX     ;move number of sectors to total
  187.         RET
  188. IN_SAVE ENDP
  189.  
  190. CALC_ADDR  PROC NEAR
  191.         MOV     AX,CS:START_SEC ;Get starting sector number
  192.         MOV     CX,20H          ;move 512 to cx segment style
  193.         MUL     CX              ;multiply to get actual sector
  194.         MOV     DX,CS:VDISK_PTR ;get segment of virtual disk
  195.         ADD     DX,AX           ;add that segment to initial segment
  196.         MOV     DS,DX           ;save that as actual segment
  197.         XOR     SI,SI           ;its on paragraph boundary
  198.         MOV     AX,CS:TOTAL     ;total number of sectors to read
  199.         MOV     CX,512          ;bytes per sector
  200.         MUL     CX              ;multiply to get copy length
  201.         OR      AX,AX           ;check for greater than 64k
  202.         JNZ     MOVE_IT
  203.         MOV     AX,0FFFFH       ;move in for 64k
  204. MOVE_IT:
  205.         XCHG    CX,AX           ;move length to cx
  206.         RET
  207. CALC_ADDR       ENDP
  208.  
  209. SECTOR_READ PROC NEAR
  210.         CALL    CALC_ADDR               ;calculate starting"sector"
  211.         MOV     ES,WORD PTR CS:USER_DTA+2 ;set destination)ES:DI)to point
  212.         MOV     DI,WORD PTR CS:USER_DTA   ; to callers dta
  213.  
  214. ;  CHECK FOR DTA WRAP IN CASE WE CAME THROUGH VIA VERIFY
  215.  
  216.         MOV     AX,DI           ;get offset of dta
  217.         ADD     AX,CX           ;add copy length to it
  218.         JNC     READ_COPY       ;carry flag=0,no wrap
  219.         MOV     AX,0FFFFH       ;maximum length
  220.         SUB     AX,DI           ;subtract dta offset from max
  221.         MOV     CX,AX           ;issue that as copy length to not wrap
  222.  
  223. READ_COPY:
  224. REP     MOVSB                   ;do the "read"
  225.         RET
  226. SECTOR_READ     ENDP
  227.  
  228. SECTOR_WRITE    PROC NEAR
  229.         CALL    CALC_ADDR       ;Calculate starting sector
  230.         PUSH    DS
  231.         POP     ES              ;Establish addressability
  232.         MOV     DI,SI           ;ES:DI point to disk
  233.         MOV     DS,WORD PTR CS:USER_DTA+2 ;DS:DI point to callers dta
  234.         MOV     SI,WORD PTR CS:USER_DTA
  235.  
  236. ;               CHECK FOR DTA WRAP
  237.  
  238.         MOV     AX,SI           ;Move dta offset to ax
  239.         ADD     AX,CX           ;add copy length to offset
  240.         JNC     WRITE_COPY      ;carry flag=0,no segment wrap
  241.         MOV     AX,0FFFFH       ;move in max copy length
  242.         SUB     AX,SI           ;subtract dta offset from max
  243.         MOV     CX,AX           ;use as new copy length to avoid wrap
  244.  
  245. WRITE_COPY:
  246. REP     MOVSB                   ;do the write
  247.         RET
  248. SECTOR_WRITE    ENDP
  249.  
  250. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  251. ;    DEVICE STRATEGY
  252. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  253.  
  254. DEV_STRATEGY:
  255.     mov     CS:RH_SEG,ES            ;save segment of request header ptr
  256.     mov     CS:RH_OFF,BX            ;save offset of    "       "     "
  257.     ret
  258.  
  259. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  260. ;  DEVICE INTERRUPT HANDLER
  261. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  262.  
  263. DEV_INT:
  264.  
  265.         CLD        ;PRESERVE MACHINE STATE ON ENTRY
  266.         PUSH    DS
  267.         PUSH    ES
  268.         PUSH    AX
  269.         PUSH    BX
  270.         PUSH    CX
  271.         PUSH    DX
  272.         PUSH    DI
  273.         PUSH    SI
  274.  
  275. ; DO THE BRANCH ACCORDING TO THE FUNCTION PASSED
  276.  
  277.         MOV     AL,ES:[BX]+2            ;Get function byte
  278.         ROL     AL,1                    ;Get offset into table
  279.         LEA     DI,FUNTAB               ;get address funtab
  280.         XOR     AH,AH
  281.         ADD     DI,AX
  282.         JMP     WORD PTR[DI]
  283.  
  284. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  285. ;    MEDIA CHECK
  286. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  287.  
  288. MEDIA_CHECK:    ;Media check (block only) - set media not changed
  289.         MOV     ES:BYTE PTR RET_BYTE[BX],1 ;store in return byte
  290.         STATUS  DONE,NOERROR,0          ;turn on done bit (macro)
  291.         JMP     EXIT
  292.  
  293. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  294. ;   BUILD BIOS PARAMETER BLOCK
  295. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  296.  
  297. BUILD_BPB:
  298.         PUSH    ES                      ;save srh segment
  299.         PUSH    BX                      ;save rh_offset
  300.         MOV     CS:WORD PTR START_SEC,0
  301.         MOV     CS:WORD PTR TOTAL,1
  302.         CALL    CALC_ADDR               ;calculate address of first sector
  303.         PUSH    CS
  304.         POP     ES
  305.         LEA     DI,WORD PTR BPB         ;address of BIOS paramter block
  306.         ADD     SI,11                   ;add 11 to si
  307.         MOV     CX,13                   ;length of bpb
  308.     REP     MOVSB
  309.         POP     BX                      ;restore offset of srh
  310.         POP     ES                      ;restore segment of srh
  311.         LEA     DX,WORD PTR BPB         ;get BPB array pointer
  312.         MOV     ES:BPBA_PTR[BX],DX      ;save pointer to BPB table
  313.         MOV     ES:BPBA_PTR+2[BX],CS
  314.         MOV     ES:DTA[BX],DX           ;offset of sector buffer
  315.         MOV     ES:DTA+2[BX],CS
  316.         STATUS  DONE,NOERROR,0          ;set status (macro)
  317.         JMP     EXIT
  318.  
  319. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  320. ;     DISK READ
  321. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  322.  
  323. INPUT:
  324.         CALL    IN_SAVE                 ;call initial save routine
  325.         MOV     AX,ES:WORD PTR SSN[BX]  ;get starting sector number
  326.         MOV     CS:START_SEC,AX         ;save starting sector number
  327.         MOV     AX,ES:WORD PTR COUNT[BX]
  328.         MOV     CS:TOTAL,AX             ;save total sectors to transfer
  329.         CALL    SECTOR_READ             ;readin that many sectors
  330.         MOV     BX,CS:RH_OFF            ;restore ES:BX as request hdr ptr
  331.         MOV     ES,CS:RH_SEG
  332.         STATUS  DONE,NOERROR,0
  333.         JMP     EXIT
  334.  
  335. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  336. ;    DISK WRITE
  337. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  338.  
  339. OUTPUT:                         ;output(write)
  340.         CALL    IN_SAVE
  341.         MOV     AX,ES:WORD PTR SSN[BX]  ;get starting sector number
  342.         MOV     CS:START_SEC,AX         ;set  "        "       "
  343.         MOV     AX,ES:WORD PTR COUNT[BX]
  344.         MOV     CS:TOTAL,AX             ;save total sectors to write
  345.         CALL    SECTOR_WRITE            ;write out those sectors
  346.         MOV     BX,CS:RH_OFF            ;restore ES:BX as request hdr ptr
  347.         MOV     ES,CS:RH_SEG
  348.         CMP     CS:BYTE PTR VERIFY,0    ;write verify set
  349.         JZ      NO_VERIFY               ;no, no write verify
  350.         MOV     CS:BYTE PTR VERIFY,0    ;reset verify indicator
  351.         JMP     INPUT
  352. NO_VERIFY:
  353.         STATUS  DONE,NOERROR,0          ;set done,noerror in statusword
  354.         JMP     EXIT
  355. OUT_VERIFY:                             ;output(write)with verify
  356.         MOV     CS:BYTE PTR VERIFY,1    ;set the verify flag
  357.         JMP     OUTPUT                  ;branch to output routine
  358.  
  359. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  360. ;  NOT SUPPORTED BY THIS DEVICE
  361. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  362.  
  363. IOCTL_IN:
  364. IOCTL_OUT:
  365. NO_INPUT:
  366. IN_STAT:
  367. IN_FLUSH:
  368. OUT_STAT: 
  369. OUT_FLUSH:
  370. DOS3:        ;fall thru to exit (does nothing)
  371.  
  372. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  373. ;      COMMON EXIT
  374. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  375.  
  376. EXIT:
  377.         POP     SI              ;restore all registers
  378.         POP     DI
  379.         POP     DX
  380.         POP     CX
  381.         POP     BX
  382.         POP     AX
  383.         POP     ES
  384.         POP     DS
  385.         RET
  386.  
  387. END_OF_DRIVER equ $        ;the end of the code that must remain
  388.                 ;what follows is used once and discarded
  389. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  390. ;  INITIALIZE DRIVER
  391. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  392.  
  393. INIT:
  394.     mov    AX,OFFSET CS:EXIT    ;kill init table entry
  395.     mov    WORD PTR CS:[FUNTAB],AX    ; (just for safety)
  396.  
  397.     push    DX
  398.     mov    DX,OFFSET CS:SIGNON    ;
  399.     mov    AH,9            ;
  400.     int    21h            ;tell user we're here
  401.     pop    DX
  402.  
  403.         mov     AX,OFFSET CS:END_OF_DRIVER  ;get the end of this driver code
  404.         mov     ES:BR_ADDR_0[BX],AX     ;break address (offset) is the end
  405.         mov     ES:WORD PTR BR_ADDR_1[BX],CS    ;code segment for break address
  406.         mov     ES:BYTE PTR UNITS[BX],1 ;number of units
  407.         lea     DX,WORD PTR BPB_PTR     ;address of bpb pointer array
  408.         mov     ES:BPB_PTR_OFF[BX],DX   ;save offset in data packet
  409.         mov     ES:BPB_PTR_SEG[BX],CS   ;save segment in data packet
  410.  
  411.         mov     ES,CS:VDISK_PTR         ;get start segment of virtual disk
  412.         xor     DI,DI                   ;DI=0 (boot sector)
  413.     push    CS
  414.     pop    DS            ;source seg is CS
  415.         lea     SI,WORD PTR CS:BOOT_REC ;address of boot record
  416.         mov     CX,11
  417.     rep    movsb            ;copy first part of boot record
  418.     lea    SI,WORD PTR CS:BPB    ;get rest of boot record from BPB
  419.     mov    CX,13            ;
  420.     rep    movsb            ;24 bytes, in all
  421.  
  422.         mov     ES,CS:VDISK_PTR    ;sector for disk
  423.         mov    DI,200h        ;second sector of disk
  424.         mov    CX,400h        ;two sectors
  425.         xor     AL,AL        ;move in nulls
  426.     rep    stosb        ;zero out FAT area
  427.  
  428.         mov     DS,CS:VDISK_PTR    ;segment for VDISK
  429.         mov     ES,CS:VDISK_PTR    ;
  430.     mov    SI,200h        ;second sector (01) is source
  431.         mov     DI,600h         ;4th sector (03) is destination
  432.         mov     DS:BYTE PTR [SI],0FCH   ;set first FAT entry
  433.         mov     DS:BYTE PTR 1[SI],0FFH
  434.         mov     DS:BYTE PTR 2[SI],0FFH
  435.         mov    CX,400h        ;2 sectors per fat
  436.     rep    movsb        ;copy first FAT to second FAT
  437.  
  438.     push    CS        ;
  439.     pop    DS        ;restore DS for print function
  440.  
  441.         mov     ES,CS:VDISK_PTR    ;VDISK segment
  442.         mov     DI,0A00h    ;start 6th sector (05)
  443.         mov    CX,800h        ;4 sectors in directory
  444.         xor     AL,AL        ;null byte for fill
  445.     rep     stosb        ;zero out directory
  446.  
  447.         mov     ES,CS:RH_SEG    ;restore ES:BX to request header
  448.         mov     BX,CS:RH_OFF
  449.         STATUS  DONE,NOERROR,0  ;set status word (Macro)
  450.  
  451.     push    DX
  452.     mov    DX, OFFSET CS:TELL5
  453.     mov    AH,9
  454.     int    21h
  455.     pop    DX        ;VDISK installation complete
  456.  
  457.         jmp     EXIT
  458.  
  459. BOOT_REC        EQU     $       ;Dummy DOS boot record
  460.         db      3 DUP(0)        ;3 Byte jump to boot code (null here)
  461.         db      8 dup (' ')     ;Vendor ID (must be 8 bytes)
  462. ;the rest of the boot record is the same as the BPB
  463.  
  464. SIGNON:
  465.     db    CR,LF,'128K Ram Disk in High Memory Pages C and D.'
  466.     db    CR,LF,'By J. D. Kronman -- 15 November 1985'
  467.     db    CR,LF
  468.     db    '$'
  469. TELL5:
  470.     db    CR,LF,'VDISK-H installed.'
  471.     db    CR,LF
  472.     db    '$'
  473.  
  474. VDSK    endp
  475. CSEG    ends
  476.         end     BEGIN
  477.